home *** CD-ROM | disk | FTP | other *** search
/ Reverse Code Engineering RCE CD +sandman 2000 / ReverseCodeEngineeringRceCdsandman2000.iso / RCE / Library / Manuels & Misc / Assembly / AOA.ZIP / CH19 / AMAZE.ASM next >
Encoding:
Assembly Source File  |  1994-07-20  |  23.9 KB  |  1,023 lines

  1. ; AMAZE.ASM
  2. ;
  3. ; A maze generation/solution program.
  4. ;
  5. ; This program generates an 80x25 maze and directly draws the maze on the
  6. ; video display.  It demonstrates the use of coroutines within a program.
  7.  
  8.         .xlist
  9.         include     stdlib.a
  10.         includelib    stdlib.lib
  11.         .list
  12.  
  13. byp        textequ    <byte ptr>
  14.  
  15. dseg        segment    para public 'data'
  16.  
  17. ; Constants:
  18. ;
  19. ; Define the "ToScreen" symbol (to any value) if the maze is 80x25 and you
  20. ; want to display it on the video screen.
  21.  
  22. ToScreen    equ    0
  23.  
  24.  
  25. ; Maximum X and Y coordinates for the maze (matching the display).
  26.  
  27. MaxXCoord    equ    80
  28. MaxYCoord    equ    25
  29.  
  30. ; Useful X,Y constants:
  31.  
  32. WordsPerRow    =    MaxXCoord+2
  33. BytesPerRow    =    WordsPerRow*2
  34.  
  35. StartX        equ    1        ;Starting X coordinate for maze
  36. StartY        equ    3        ;Starting Y coordinate for maze
  37. EndX        equ    MaxXCoord    ;Ending X coordinate for maze
  38. EndY        equ    MaxYCoord-1    ;Ending Y coordinate for maze
  39.  
  40. EndLoc        =    ( (EndY-1)*MaxXCoord + EndX-1)*2
  41. StartLoc    =    ( (StartY-1)*MaxXCoord + StartX-1)*2
  42.  
  43. ; Special 16-bit PC character codes for the screen for symbols drawn during
  44. ; maze generation.  See the chapter on the video display for details.
  45.  
  46.         ifdef    mono        ;Mono display adapter.
  47.  
  48. WallChar    equ    7dbh        ;Solid block character
  49. NoWallChar    equ    720h        ;space
  50. VisitChar    equ    72eh        ;Period
  51. PathChar    equ    72ah        ;Asterisk
  52.  
  53.         else            ;Color display adapter.
  54.  
  55. WallChar    equ    1dbh        ;Solid block character
  56. NoWallChar    equ    0edbh        ;space
  57. VisitChar    equ    0bdbh        ;Period
  58. PathChar    equ    4e2ah        ;Asterisk
  59.  
  60.         endif
  61.  
  62.  
  63.  
  64.  
  65. ; The following are the constants that may appear in the Maze array:
  66.  
  67. Wall        =    0
  68. NoWall        =    1
  69. Visited        =    2
  70.  
  71. ; The following are the directions the demons can go in the maze
  72.  
  73. North        =    0
  74. South        =    1
  75. East        =    2
  76. West        =    3
  77.  
  78.  
  79. ; Some important variables:
  80.  
  81.  
  82. ; The Maze array must contain an extra row and column around the
  83. ; outside edges for our algorithm to work properly.
  84.  
  85. Maze        word    (MaxYCoord+2) dup ((MaxXCoord+2) dup (Wall))
  86.  
  87. ; The follow macro computes an index into the above array assuming
  88. ; a demon's X and Y coordinates are in the dl and dh registers, respectively.
  89. ; Returns index in the AX register
  90.  
  91. MazeAdrs    macro
  92.         mov    al, dh
  93.         mov    ah, WordsPerRow        ;Index into array is computed
  94.         mul    ah            ; by (Y*words/row + X)*2.
  95.         add    al, dl
  96.         adc    ah, 0
  97.         shl    ax, 1            ;Convert to byte index
  98.         endm
  99.  
  100. ; The following macro computes an index into the screen array, using the
  101. ; same assumptions as above.  Note that the screen matrix is 80x25 whereas
  102. ; the maze matrix is 82x27;  The X/Y coordinates in DL/DH are 1..80 and
  103. ; 1..25 rather than 0..79 and 0..24 (like we need).  This macro adjusts
  104. ; for that.
  105.  
  106. ScrnAdrs    macro
  107.         mov    al, dh
  108.         dec    al
  109.         mov    ah, MaxXCoord
  110.         mul    ah
  111.         add    al, dl
  112.         adc    ah, 0
  113.         dec    ax
  114.         shl    ax, 1
  115.         endm
  116.  
  117.  
  118.  
  119. ; PCB for the main program.  The last live demon will call this guy when
  120. ; it dies.
  121.  
  122. MainPCB        pcb    {}
  123.  
  124.  
  125. ; List of up to 32 demons.
  126.  
  127. MaxDemons    =    32            ;Must be a power of two.
  128. ModDemons    =    MaxDemons-1        ;Mask for MOD computation.
  129.  
  130. DemonList    pcb    MaxDemons dup ({})
  131.  
  132. DemonIndex    byte    0            ;Index into demon list.
  133. DemonCnt    byte    0            ;Number of demons in list.
  134.  
  135.  
  136. ; Random number generator seed (we'll use our random number generator
  137. ; rather than the standard library's because we want to be able to specify
  138. ; an initial seed value).
  139.  
  140. Seed        word    0
  141.  
  142. dseg        ends
  143.  
  144.  
  145.  
  146. ; The following is the segment address of the video display, change this
  147. ; from 0B800h to 0B000h if you have a monochrome display rather than a
  148. ; color display.
  149.  
  150. ScreenSeg    segment    at 0b800h
  151. Screen        equ    this word    ;Don't generate in date here!
  152. ScreenSeg    ends
  153.  
  154.  
  155. cseg        segment    para public 'code'
  156.         assume    cs:cseg, ds:dseg
  157.  
  158. ; Totally bogus random number generator, but we don't need a really
  159. ; great one for this program.  This code uses its own random number
  160. ; generator rather than the one in the Standard Library so we can
  161. ; allow the user to use a fixed seed to produce the same maze (with
  162. ; the same seed) or different mazes (by choosing different seeds).
  163.  
  164. RandNum        proc    near
  165.         push    cx
  166.         mov    cl, byte ptr Seed
  167.         and    cl, 7
  168.         add    cl, 4
  169.         mov    ax, Seed
  170.         xor    ax, 55aah
  171.         rol    ax, cl
  172.         xor    ax, Seed
  173.         inc    ax
  174.         mov    Seed, ax
  175.         pop    cx
  176.         ret
  177. RandNum        endp
  178.  
  179. ; Init-    Handles all the initialization chores for the main program.
  180. ;    In particular, it initializes the coroutine package, gets a
  181. ;    random number seed from the user, and initializes the video display.
  182.  
  183. Init        proc    near
  184.         print
  185.         byte    "Enter a small integer for a random number seed:",0
  186.         getsm
  187.         atoi
  188.         free
  189.         mov    Seed, ax
  190.  
  191. ; Fill the interior of the maze with wall characters, fill the outside
  192. ; two rows and columns with nowall values.  This will prevent the demons
  193. ; from wandering outside the maze.
  194.  
  195.  
  196. ; Fill the first row with Visited values.
  197.  
  198.         cld
  199.         mov    cx, WordsPerRow
  200.         lesi    Maze
  201.         mov    ax, Visited
  202.     rep    stosw
  203.  
  204. ; Fill the last row with NoWall values.
  205.  
  206.         mov    cx, WordsPerRow
  207.         lea    di, Maze+(MaxYCoord+1)*BytesPerRow
  208.     rep    stosw
  209.  
  210. ; Write a NoWall value to the starting position:
  211.  
  212.         mov    Maze+(StartY*WordsPerRow+StartX)*2, NoWall
  213.  
  214.  
  215. ; Write NoWall values along the two vertical edges of the maze.
  216.  
  217.         lesi    Maze
  218.         mov    cx, MaxYCoord+1
  219. EdgesLoop:    mov    es:[di], ax            ;Plug the left edge.
  220.         mov    es:[di+BytesPerRow-2], ax    ;Plug the right edge.
  221.         add    di, BytesPerRow
  222.         loop    EdgesLoop
  223.  
  224.  
  225.         ifdef    ToScreen
  226.  
  227. ; Okay, fill the screen with WallChar values:
  228.  
  229.         lesi    Screen
  230.         mov    ax, WallChar
  231.         mov    cx, 2000
  232.     rep    stosw
  233.  
  234. ; Write appropriate characters to the starting and ending locations:
  235.  
  236.         mov    word ptr es:Screen+EndLoc, PathChar
  237.         mov    word ptr es:Screen+StartLoc, NoWallChar
  238.  
  239.         endif    ;ToScreen
  240.  
  241.  
  242. ; Zero out the DemonList:
  243.  
  244.         mov    cx, (size pcb)*MaxDemons
  245.         lea    di, DemonList
  246.         mov    ax, dseg
  247.         mov    es, ax
  248.         xor    ax, ax
  249.     rep    stosb
  250.  
  251.         ret
  252. Init        endp
  253.  
  254.  
  255.  
  256. ; CanStart- This function checks around the current position
  257. ; to see if the maze generator can start digging a new tunnel
  258. ; in a direction perpendicular to the current tunnel.  You can
  259. ; only start a new tunnel if there are wall characters for at
  260. ; least two positions in the desired direction:
  261. ;
  262. ;            ##
  263. ;               *##
  264. ;            ##
  265. ;
  266. ; If "*" is current position and "#" represent wall characters
  267. ; and the current direction is north or south, then it is okay
  268. ; for the maze generator to start a new path in the east dir-
  269. ; ection.  Assuming "." represents a tunnel, you cannot start
  270. ; a new tunnel in the east direction if any of the following
  271. ; patterns occur:
  272. ;
  273. ;        .#    #.    ##    ##    ##    ##
  274. ;           *##     *##     *.#     *#.     *##     *##
  275. ;        ##    ##    ##    ##    .#    #.
  276. ;
  277. ; CanStart returns true (carry set) if we can start a new tunnel off the
  278. ; path being dug by the current demon.
  279. ;
  280. ; On entry,     dl is demon's X-Coordinate
  281. ;               dh is demon's Y-Coordinate
  282. ;        cl is demon's direction
  283.  
  284. CanStart    proc    near
  285.         push    ax
  286.         push    bx
  287.  
  288.         MazeAdrs        ;Compute index to demon(x,y) in maze.
  289.         mov    bx, ax
  290.  
  291. ; CL contains the current direction, 0=north, 1=south, 2=east, 3=west.
  292. ; Note that we can test bit #1 for north/south (0) or east/west (1).
  293.  
  294.         test    cl, 10b        ;See if north/south or east/west
  295.         jz    NorthSouth
  296.  
  297. ; If the demon is going in an east or west direction, we can start a new
  298. ; tunnel if there are six wall blocks just above or below the current demon.
  299. ; Note: We are checking if all values in these six blocks are Wall values.
  300. ; This code depends on the fact that Wall characters are zero and the sum
  301. ; of these six blocks will be zero if a move is possible.
  302.  
  303.         mov    al, byp Maze[bx+BytesPerRow*2]     ;Maze[x,  y+2]
  304.         add    al, byp Maze[bx+BytesPerRow*2+2] ;Maze[x+1,y+2]
  305.         add    al, byp Maze[bx+BytesPerRow*2-2] ;Maze[x-1,y+2]
  306.         je    ReturnTrue
  307.  
  308.         mov    al, byp Maze[bx-BytesPerRow*2]     ;Maze[x,  y-2]
  309.         add    al, byp Maze[bx-BytesPerRow*2+2] ;Maze[x+1,y-2]
  310.         add    al, byp Maze[bx-BytesPerRow*2-2] ;Maze[x-1,y-2]
  311.         je    ReturnTrue
  312.  
  313. ReturnFalse:    clc                ;Clear carry = false.
  314.         pop    bx
  315.         pop    ax
  316.         ret
  317.  
  318. ; If the demon is going in a north or south direction, we can start a
  319. ; new tunnel if there are six wall blocks just to the left or right
  320. ; of the current demon.
  321.  
  322. NorthSouth:    mov    al, byp Maze[bx+4]        ;Maze[x+2,y]
  323.         add    al, byp Maze[bx+BytesPerRow+4]    ;Maze[x+2,y+1]
  324.         add    al, byp Maze[bx-BytesPerRow+4]    ;Maze[x+2,y-1]
  325.         je    ReturnTrue
  326.  
  327.         mov    al, byp Maze[bx-4]        ;Maze[x-2,y]
  328.         add    al, byp Maze[bx+BytesPerRow-4]    ;Maze[x-2,y+1]
  329.         add    al, byp Maze[bx-BytesPerRow-4]    ;Maze[x-2,y-1]
  330.         jne    ReturnFalse
  331.  
  332. ReturnTrue:    stc                ;Set carry = true.
  333.         pop    bx
  334.         pop    ax
  335.         ret
  336. CanStart    endp
  337.  
  338.  
  339.  
  340.  
  341. ; CanMove-    Tests to see if the current demon (dir=cl, x=dl, y=dh) can
  342. ;        move in the specified direction.  Movement is possible if
  343. ;        the demon will not come within one square of another tunnel.
  344. ;        This function returns true (carry set) if a move is possible.
  345. ;        On entry, CH contains the direction this code should test.
  346.  
  347. CanMove        proc
  348.         push    ax
  349.         push    bx
  350.  
  351.         MazeAdrs            ;Put @Maze[x,y] into ax.
  352.         mov    bx, ax
  353.  
  354.         cmp    ch, South
  355.         jb    IsNorth
  356.         je    IsSouth
  357.         cmp    ch, East
  358.         je    IsEast
  359.  
  360. ; If the demon is moving west, check the blocks in the rectangle formed
  361. ; by Maze[x-2,y-1] to Maze[x-1,y+1] to make sure they are all wall values.
  362.  
  363.         mov    al, byp Maze[bx-BytesPerRow-4]    ;Maze[x-2, y-1]
  364.         add    al, byp Maze[bx-BytesPerRow-2]    ;Maze[x-1, y-1]
  365.         add    al, byp Maze[bx-4]        ;Maze[x-2, y]
  366.         add    al, byp Maze[bx-2]        ;Maze[x-1, y]
  367.         add    al, byp Maze[bx+BytesPerRow-4]    ;Maze[x-2, y+1]
  368.         add    al, byp Maze[bx+BytesPerRow-2]    ;Maze[x-1, y+1]
  369.         je    ReturnTrue
  370. ReturnFalse:    clc
  371.         pop    bx
  372.         pop    ax
  373.         ret
  374.  
  375.  
  376. ; If the demon is going east, check the blocks in the rectangle formed
  377. ; by Maze[x+1,y-1] to Maze[x+2,y+1] to make sure they are all wall values.
  378.  
  379. IsEast:        mov    al, byp Maze[bx-BytesPerRow+4]    ;Maze[x+2, y-1]
  380.         add    al, byp Maze[bx-BytesPerRow+2]    ;Maze[x+1, y-1]
  381.         add    al, byp Maze[bx+4]        ;Maze[x+2, y]
  382.         add    al, byp Maze[bx+2]        ;Maze[x+1, y]
  383.         add    al, byp Maze[bx+BytesPerRow+4]    ;Maze[x+2, y+1]
  384.         add    al, byp Maze[bx+BytesPerRow+2]    ;Maze[x+1, y+1]
  385.         jne    ReturnFalse
  386. ReturnTrue:    stc
  387.         pop    bx
  388.         pop    ax
  389.         ret
  390.  
  391.  
  392. ; If the demon is going north, check the blocks in the rectangle formed
  393. ; by Maze[x-1,y-2] to Maze[x+1,y-1] to make sure they are all wall values.
  394.  
  395. IsNorth:    mov    al, byp Maze[bx-BytesPerRow-2]    ;Maze[x-1, y-1]
  396.         add    al, byp Maze[bx-BytesPerRow*2-2];Maze[x-1, y-2]
  397.         add    al, byp Maze[bx-BytesPerRow]    ;Maze[x,   y-1]
  398.         add    al, byp Maze[bx-BytesPerRow*2]    ;Maze[x,   y-2]
  399.         add    al, byp Maze[bx-BytesPerRow+2]    ;Maze[x+1, y-1]
  400.         add    al, byp Maze[bx-BytesPerRow*2+2];Maze[x+1, y-2]
  401.         jne    ReturnFalse
  402.         stc
  403.         pop    bx
  404.         pop    ax
  405.         ret
  406.  
  407.  
  408.  
  409. ; If the demon is going south, check the blocks in the rectangle formed
  410. ; by Maze[x-1,y+2] to Maze[x+1,y+1] to make sure they are all wall values.
  411.  
  412. IsSouth:    mov    al, byp Maze[bx+BytesPerRow-2]    ;Maze[x-1, y+1]
  413.         add    al, byp Maze[bx+BytesPerRow*2-2];Maze[x-1, y+2]
  414.         add    al, byp Maze[bx+BytesPerRow]    ;Maze[x,   y+1]
  415.         add    al, byp Maze[bx+BytesPerRow*2]    ;Maze[x,   y+2]
  416.         add    al, byp Maze[bx+BytesPerRow+2]    ;Maze[x+1, y+1]
  417.         add    al, byp Maze[bx+BytesPerRow*2+2];Maze[x+1, y+2]
  418.         jne    ReturnFalse
  419.         stc
  420.         pop    bx
  421.         pop    ax
  422.         ret
  423.  
  424. CanMove        endp
  425.  
  426.  
  427.  
  428.  
  429. ; SetDir- Changes the current direction.  The maze digging algorithm has
  430. ; decided to change the direction of the tunnel begin dug by one
  431. ; of the demons.  This code checks to see if we CAN change the direction,
  432. ; and picks a new direction if possible.
  433. ;
  434. ; If the demon is going north or south, a direction change causes the demon
  435. ; to go east or west.  Likewise, if the demon is going east or west, a
  436. ; direction change forces it to go north or south.  If the demon cannot
  437. ; change directions (because it cannot move in the new direction for one
  438. ; reason or another), SetDir returns without doing anything.  If a direction
  439. ; change is possible, then SetDir selects a new direction.  If there is only
  440. ; one possible new direction, the demon is sent off in that direction.
  441. ; If the demon could move off in one of two different directions, SetDir
  442. ; "flips a coin" to choose one of the two new directions.
  443. ;
  444. ; This function returns the new direction in al.
  445.  
  446. SetDir        proc    near
  447.  
  448.         test    cl, 10b            ;See if north/south
  449.         je    IsNS            ; or east/west direction.
  450.  
  451. ; We're going east or west.  If we can move EITHER north or south from
  452. ; this point, randomly choose one of the directions.  If we can only
  453. ; move one way or the other, choose that direction.  If we can't go either
  454. ; way, return without changing the direction.
  455.  
  456.         mov    ch, North        ;See if we can move north
  457.         call    CanMove
  458.         jnc    NotNorth
  459.         mov    ch, South        ;See if we can move south
  460.         call    CanMove
  461.         jnc    DoNorth
  462.         call    RandNum            ;Get a random direction
  463.         and    ax, 1            ;Make it north or south.
  464.         ret
  465.  
  466. DoNorth:    mov    ax, North
  467.         ret
  468.  
  469. NotNorth:    mov    ch, South
  470.         call    CanMove
  471.         jnc    TryReverse
  472. DoSouth:    mov    ax, South
  473.         ret
  474.  
  475.  
  476.  
  477. ; If the demon is moving north or south, choose a new direction of east
  478. ; or west, if possible.
  479.  
  480. IsNS:        mov    ch, East        ;See if we can move East
  481.         call    CanMove
  482.         jnc    NotEast
  483.         mov    ch, West        ;See if we can move West
  484.         call    CanMove
  485.         jnc    DoEast
  486.         call    RandNum            ;Get a random direction
  487.         and    ax, 1b            ;Make it East or West
  488.         or    al, 10b
  489.         ret
  490.  
  491. DoEast:        mov    ax, East
  492.         ret
  493.  
  494. DoWest:        mov    ax, West
  495.         ret
  496.  
  497. NotEast:    mov    ch, West
  498.         call    CanMove
  499.         jc    DoWest
  500.  
  501. ; Gee, we can't switch to a perpendicular direction, see if we can
  502. ; turn around.
  503.  
  504. TryReverse:    mov    ch, cl
  505.         xor    ch, 1
  506.         call    CanMove
  507.         jc    ReverseDir
  508.  
  509. ; If we can't turn around (likely), then keep going in the same direction.
  510.  
  511.         mov    ah, 0
  512.         mov    al, cl            ;Stay in same direction.
  513.         ret
  514.  
  515. ; Otherwise reverse direction down here.
  516.  
  517. ReverseDir:    mov    ah, 0
  518.         mov    al, cl
  519.         xor    al, 1
  520.         ret
  521. SetDir        endp
  522.  
  523.  
  524.  
  525. ; Stuck-    This function checks to see if a demon is stuck and cannot
  526. ;        move in any direction.  It returns true if the demon is
  527. ;        stuck and needs to be killed.
  528.  
  529. Stuck        proc    near
  530.         mov    ch, North
  531.         call    CanMove
  532.         jc    NotStuck
  533.         mov    ch, South
  534.         call    CanMove
  535.         jc    NotStuck
  536.         mov    ch, East
  537.         call    CanMove
  538.         jc    NotStuck
  539.         mov    ch, West
  540.         call    CanMove
  541. NotStuck:    ret
  542. Stuck        endp
  543.  
  544.  
  545.  
  546. ; NextDemon-    Searches through the demon list to find the next available
  547. ;        active demon.  Return a pointer to this guy in es:di.
  548.  
  549. NextDemon    proc    near
  550.         push    ax
  551.  
  552. NDLoop:        inc    DemonIndex        ;Move on to next demon,
  553.         and    DemonIndex, ModDemons    ; MOD MaxDemons.
  554.         mov    al, size pcb        ;Compute index into
  555.         mul    DemonIndex        ; DemonList.
  556.         mov    di, ax            ;See if the demon at this
  557.         add    di, offset DemonList    ; offset is active.
  558.         cmp    byp [di].pcb.NextProc, 0
  559.         je    NDLoop
  560.  
  561.         mov    ax, ds
  562.         mov    es, ax
  563.         pop    ax
  564.         ret
  565. NextDemon    endp
  566.  
  567.  
  568.  
  569. ; Dig-        This is the demon process.
  570. ;        It moves the demon one position (if possible) in its current
  571. ;        direction.  After moving one position forward, there is
  572. ;        a 25% chance that this guy will change its direction; there
  573. ;        is a 25% chance this demon will spawn a child process to
  574. ;        dig off in a perpendicular direction.
  575.  
  576. Dig        proc    near
  577.  
  578. ; See if the current demon is stuck.  If the demon is stuck, then we've
  579. ; go to remove it from the demon list.  If it is not stuck, then have it
  580. ; continue digging.  If it is stuck and this is the last active demon,
  581. ; then return control to the main program.
  582.  
  583.         call    Stuck
  584.         jc    NotStuck
  585.  
  586. ; Okay, kill the current demon.
  587. ; Note: this will never kill the last demon because we have the timer
  588. ; process running.  The timer process is the one that always stops
  589. ; the program.
  590.  
  591.         dec    DemonCnt
  592.  
  593. ; Since the count is not zero, there must be more demons in the demon
  594. ; list.  Free the stack space associated with the current demon and
  595. ; then search out the next active demon and have at it.
  596.  
  597. MoreDemons:    mov    al, size pcb
  598.         mul    DemonIndex
  599.         mov    bx, ax
  600.  
  601. ; Free the stack space associated with this process.  Note this code is
  602. ; naughty.  It assumes the stack is allocated with the Standard Library
  603. ; malloc routine that always produces a base address of 8.
  604.  
  605.         mov    es, DemonList[bx].regss
  606.         mov    di, 8                ;Cheating!
  607.         free
  608.  
  609. ; Mark the demon entry for this guy as unused.
  610.  
  611.         mov    byp DemonList[bx].NextProc, 0    ;Mark as unused.
  612.  
  613.  
  614. ; Okay, locate the next active demon in the list.
  615.  
  616. FndNxtDmn:    call    NextDemon
  617.         cocall                ;Never returns
  618.  
  619.  
  620.  
  621.  
  622. ; If the demon is not stuck, then continue digging away.
  623.  
  624. NotStuck:    mov    ch, cl
  625.         call    CanMove
  626.         jnc    DontMove
  627.  
  628. ; If we can move, then adjust the demon's coordinates appropriately:
  629.  
  630.         cmp    cl, South
  631.         jb    MoveNorth
  632.         je    MoveSouth
  633.         cmp    cl, East
  634.         jne    MoveWest
  635.  
  636. ; Moving East:
  637.  
  638.         inc    dl
  639.         jmp    MoveDone
  640.  
  641. MoveWest:    dec    dl
  642.         jmp    MoveDone
  643.  
  644. MoveNorth:    dec    dh
  645.         jmp    MoveDone
  646.  
  647. MoveSouth:    inc    dh
  648.  
  649. ; Okay, store a NoWall value at this entry in the maze and output a NoWall
  650. ; character to the screen (if writing data to the screen).
  651.  
  652. MoveDone:    MazeAdrs
  653.         mov    bx, ax
  654.         mov    Maze[bx], NoWall
  655.  
  656.         ifdef    ToScreen
  657.         ScrnAdrs
  658.         mov    bx, ax
  659.         push    es
  660.         mov    ax, ScreenSeg
  661.         mov    es, ax
  662.         mov    word ptr es:[bx], NoWallChar
  663.         pop    es
  664.         endif
  665.  
  666. ; Before leaving, see if this demon shouldn't change direction.
  667.  
  668. DontMove:    call    RandNum
  669.         and    al, 11b            ;25% chance result is zero.
  670.         jne    NoChangeDir
  671.         call    SetDir
  672.         mov    cl, al
  673.  
  674. NoChangeDir:
  675.  
  676.  
  677. ; Also, see if this demon should spawn a child process
  678.  
  679.         call    RandNum
  680.         and    al, 11b            ;Give it a 25% chance.
  681.         jne    NoSpawn
  682.  
  683. ; Okay, see if it's possible to spawn a new process at this point:
  684.  
  685.         call    CanStart
  686.         jnc    NoSpawn
  687.  
  688. ; See if we've already got MaxDemons active:
  689.  
  690.         cmp    DemonCnt, MaxDemons
  691.         jae    NoSpawn
  692.  
  693.         inc    DemonCnt            ;Add another demon.
  694.  
  695.  
  696. ; Okay, create a new demon and add him to the list.
  697.  
  698.         push    dx                ;Save cur demon info.
  699.         push    cx
  700.  
  701. ; Locate a free slot for this demon
  702.  
  703.         lea    si, DemonList- size pcb
  704. FindSlot:    add    si, size pcb
  705.         cmp    byp [si].pcb.NextProc, 0
  706.         jne    FindSlot
  707.  
  708.  
  709. ; Allocate some stack space for the new demon.
  710.  
  711.         mov    cx, 256                ;256 byte stack.
  712.         malloc
  713.  
  714. ; Set up the stack pointer for this guy:
  715.  
  716.         add    di, 248                ;Point stack at end.
  717.         mov    [si].pcb.regss, es
  718.         mov    [si].pcb.regsp, di
  719.  
  720. ; Set up the execution address for this guy:
  721.  
  722.         mov    [si].pcb.regcs, cs
  723.         mov    [si].pcb.regip, offset Dig
  724.  
  725. ; Initial coordinates and direction for this guy:
  726.  
  727.         mov    [si].pcb.regdx, dx
  728.  
  729. ; Select a direction for this guy.
  730.  
  731.         pop    cx            ;Retrieve direction.
  732.         push    cx
  733.  
  734.         call    SetDir
  735.         mov    ah, 0
  736.         mov    [si].pcb.regcx, ax
  737.  
  738. ; Set up other misc junk:
  739.  
  740.         mov    [si].pcb.regds, seg dseg
  741.         sti
  742.         pushf
  743.         pop    [si].pcb.regflags
  744.         mov    byp [si].pcb.NextProc, 1    ;Mark active.
  745.  
  746.  
  747. ; Restore current process' parameters
  748.  
  749.         pop    cx            ;Restore current demon.
  750.         pop    dx
  751.  
  752. NoSpawn:
  753.  
  754. ; Okay, with all of the above done, it's time to pass control on to a new
  755. ; digger.  The following cocall passes control to the next digger in the
  756. ; DemonList.
  757.  
  758. GetNextDmn:    call    NextDemon
  759.  
  760. ; Okay, we've got a pointer to the next demon in the list (might be the
  761. ; same demon if there's only one), pass control to that demon.
  762.  
  763.         cocall
  764.         jmp    Dig
  765. Dig        endp
  766.  
  767.  
  768. ; TimerDemon-    This demon introduces a 1/18th second delay between
  769. ;        each cycle in the demon list.  This slows down the
  770. ;        maze generation so you can see the maze being built
  771. ;        (which makes the program more interesting to watch).
  772.  
  773. TimerDemon    proc    near
  774.         push    es
  775.         push    ax
  776.  
  777.         mov    ax, 40h            ;BIOS variable area
  778.         mov    es, ax
  779.         mov    ax, es:[6Ch]        ;BIOS timer location
  780. Wait4Change:    cmp    ax, es:[6Ch]        ;BIOS changes this every
  781.         je    Wait4Change        ; 1/18th second.
  782.  
  783.         cmp    DemonCnt, 1
  784.         je    QuitProgram
  785.         pop    es
  786.         pop    ax
  787.         call    NextDemon
  788.         cocall
  789.         jmp    TimerDemon
  790.  
  791. QuitProgram:    cocall    MainPCB            ;Quit the program
  792. TimerDemon    endp
  793.  
  794.  
  795.  
  796.  
  797. ; What good is a maze generator program if it cannot solve the mazes it
  798. ; creates?  SolveMaze finds the solution (if any) for this maze.  It marks
  799. ; the solution path and the paths it tried, but failed on.
  800. ;
  801. ; function solvemaze(x,y:integer):boolean
  802.  
  803. sm_X        textequ    <[bp+6]>
  804. sm_Y        textequ    <[bp+4]>
  805.  
  806. SolveMaze    proc    near
  807.         push    bp
  808.         mov    bp, sp
  809.  
  810. ; See if we've just solved the maze:
  811.  
  812.         cmp    byte ptr sm_X, EndX
  813.         jne    NotSolved
  814.         cmp    byte ptr sm_Y, EndY
  815.         jne    NotSolved
  816.         mov    ax, 1            ;Return true.
  817.         pop    bp
  818.         ret    4
  819.  
  820. ; See if moving to this spot was an illegal move.  There will be
  821. ; a NoWall value at this cell in the maze if the move is legal.
  822.  
  823. NotSolved:    mov    dl, sm_X
  824.         mov    dh, sm_Y
  825.         MazeAdrs
  826.         mov    bx, ax
  827.         cmp    Maze[bx], NoWall
  828.         je    MoveOK
  829.         mov    ax, 0            ;Return failure
  830.         pop    bp
  831.         ret    4
  832.  
  833. ; Well, it is possible to move to this point, so place an appropriate
  834. ; value on the screen and keep searching for the solution.
  835.  
  836. MoveOK:        mov    Maze[bx], Visited
  837.  
  838.         ifdef    ToScreen
  839.         push    es            ;Write a "VisitChar"
  840.         ScrnAdrs            ; character to the
  841.         mov    bx, ax            ; screen at this X,Y
  842.         mov    ax, ScreenSeg        ; position.
  843.         mov    es, ax
  844.         mov    word ptr es:[bx], VisitChar
  845.         pop    es
  846.         endif
  847.  
  848. ; Recusively call SolveMaze until we get a solution.  Just call SolveMaze
  849. ; for the four possible directions (up, down, left, right) we could go.
  850. ; Since we've left "Visited" values in the Maze, we will not accidentally
  851. ; search back through the path we've already travelled.  Furthermore, if
  852. ; we cannot go in one of the four directions, SolveMaze will catch this
  853. ; immediately upon entry (see the code at the start of this routine).
  854.  
  855.         mov    ax, sm_X        ;Try the path at location
  856.         dec    ax            ; (X-1, Y)
  857.         push    ax
  858.         push    sm_Y
  859.         call    SolveMaze
  860.         test    ax, ax            ;Solution?
  861.         jne    Solved
  862.  
  863.         push    sm_X            ;Try the path at location
  864.         mov    ax, sm_Y        ; (X, Y-1)
  865.         dec    ax
  866.         push    ax
  867.         call    SolveMaze
  868.         test    ax, ax            ;Solution?
  869.         jne    Solved
  870.  
  871.         mov    ax, sm_X        ;Try the path at location
  872.         inc    ax            ; (X+1, Y)
  873.         push    ax
  874.         push    sm_Y
  875.         call    SolveMaze
  876.         test    ax, ax            ;Solution?
  877.         jne    Solved
  878.  
  879.         push    sm_X            ;Try the path at location
  880.         mov    ax, sm_Y        ; (X, Y+1)
  881.         inc    ax
  882.         push    ax
  883.         call    SolveMaze
  884.         test    ax, ax            ;Solution?
  885.         jne    Solved
  886.         pop    bp
  887.         ret    4
  888.  
  889. Solved:
  890.         ifdef    ToScreen        ;Draw return path.
  891.         push    es
  892.         mov    dl, sm_X
  893.         mov    dh, sm_Y
  894.         ScrnAdrs
  895.         mov    bx, ax
  896.         mov    ax, ScreenSeg
  897.         mov    es, ax
  898.         mov    word ptr es:[bx], PathChar
  899.         pop    es
  900.         mov    ax, 1            ;Return true
  901.         endif
  902.  
  903.         pop    bp
  904.         ret    4
  905. SolveMaze    endp
  906.  
  907.  
  908.  
  909. ; Here's the main program that drives the whole thing:
  910.  
  911. Main        proc
  912.         mov    ax, dseg
  913.         mov    ds, ax
  914.         mov    es, ax
  915.         meminit
  916.  
  917.  
  918.         call    Init            ;Initialize maze stuff.
  919.         lesi    MainPCB            ;Initialize coroutine
  920.         coinit                ; package.
  921.  
  922. ; Create the first demon.
  923. ; Set up the stack pointer for this guy:
  924.  
  925.         mov    cx, 256
  926.         malloc
  927.         add    di, 248
  928.         mov    DemonList.regsp, di
  929.         mov    DemonList.regss, es
  930.  
  931. ; Set up the execution address for this guy:
  932.  
  933.         mov    DemonList.regcs, cs
  934.         mov    DemonList.regip, offset Dig
  935.  
  936. ; Initial coordinates and direction for this guy:
  937.  
  938.         mov    cx, East        ;Start off going east.
  939.         mov    dh, StartY
  940.         mov    dl, StartX
  941.         mov    DemonList.regcx, cx
  942.         mov    DemonList.regdx, dx
  943.  
  944. ; Set up other misc junk:
  945.  
  946.         mov    DemonList.regds, seg dseg
  947.         sti
  948.         pushf
  949.         pop    DemonList.regflags
  950.         mov    byp DemonList.NextProc, 1    ;Demon is "active".
  951.         inc    DemonCnt
  952.         mov    DemonIndex, 0
  953.  
  954.  
  955.  
  956.  
  957. ; Set up the Timer demon:
  958.  
  959.         mov    DemonList.regsp+(size pcb), offset EndTimerStk
  960.         mov    DemonList.regss+(size pcb), ss
  961.  
  962. ; Set up the execution address for this guy:
  963.  
  964.         mov    DemonList.regcs+(size pcb), cs
  965.         mov    DemonList.regip+(size pcb), offset TimerDemon
  966.  
  967. ; Set up other misc junk:
  968.  
  969.         mov    DemonList.regds+(size pcb), seg dseg
  970.         sti
  971.         pushf
  972.         pop    DemonList.regflags+(size pcb)
  973.         mov    byp DemonList.NextProc+(size pcb), 1
  974.         inc    DemonCnt
  975.  
  976. ; Start the ball rolling.
  977.  
  978.         mov    ax, ds
  979.         mov    es, ax
  980.         lea    di, DemonList
  981.         cocall
  982.  
  983. ; Wait for the user to press a key before solving the maze:
  984.  
  985.         getc
  986.  
  987.         mov    ax, StartX
  988.         push    ax
  989.         mov    ax, StartY
  990.         push    ax
  991.         call    SolveMaze
  992.  
  993. ; Wait for another keystroke before quitting:
  994.  
  995.         getc
  996.  
  997.         mov    ax, 3        ;Clear screen and reset video mode.
  998.         int    10h
  999.  
  1000. Quit:        ExitPgm            ;DOS macro to quit program.
  1001. Main        endp
  1002.  
  1003. cseg        ends
  1004.  
  1005. sseg        segment    para stack 'stack'
  1006.  
  1007. ; Stack for the timer demon we create (we'll allocate the other
  1008. ; stacks dynamically).
  1009.  
  1010. TimerStk    byte    256 dup (?)
  1011. EndTimerStk    word    ?
  1012.  
  1013.  
  1014. ; Main program's stack:
  1015.  
  1016. stk        byte    512 dup (?)
  1017. sseg        ends
  1018.  
  1019. zzzzzzseg    segment    para public 'zzzzzz'
  1020. LastBytes    db    16 dup (?)
  1021. zzzzzzseg    ends
  1022.         end    Main
  1023.